package com.nulldev.util.web.nTube.pages;

import java.util.Map;

import com.nulldev.util.data.Variables;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.experiments.Experiments;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.URLUtils.URLQueryParser;
import com.nulldev.util.web.URLUtils.URLQueryParser.URLQueryParserOptions;
import com.nulldev.util.web.YouTubeExtractor.search.YouTubeSearch;
import com.nulldev.util.web.YouTubeExtractor.search.YouTubeSearch.YouTubeSearchQuery;
import com.nulldev.util.web.YouTubeExtractor.search.YouTubeSearch.YouTubeSearchResult;
import com.nulldev.util.web.YouTubeExtractor.search.YouTubeSearch.YouTubeSearchResult.ResultType;

public class SearchVideoAPIPage extends HttpPage3 {

	private static final Logger log = LoggerFactory.getLogger();
	private static final boolean ENABLE_SECURITY_HARDENING = Experiments.get("ntube://enable-security-hardening/").isActive();

	public SearchVideoAPIPage() {
		super("/search_video_api");
		super.setChecks(EnhancedSecurityChecks.ONLY_ALLOW_BASIC_METHODS);
		super.setUseJSONCaptchaPage(true);
		super.setCachingMethod(CachingMethod.NO_CACHE);
	}

	private static final String JSON_MIME = "application/json; charset=utf-8";

	@SuppressWarnings("resource")
	@Override
	public Response onRequest(final Request request) {
		final String args = request.url();
		if (args == null || args.isEmpty()) {
			return new Response(200, createStream("{\"error\":\"invalid_data\"}")).setCustomMime(JSON_MIME);
		}
		if (args.length() > 4096 && ENABLE_SECURITY_HARDENING) {
			return new Response(200, createStream("{\"error\":\"data_too_long\"}")).setCustomMime(JSON_MIME);
		}
		final Map<String, String> arg = URLQueryParser.extract(args, URLQueryParserOptions.PREFER_LEGACY_IF_ACCEL_V1, URLQueryParserOptions.DISABLE_DECODING);
		if (!arg.containsKey("query")) {
			final UniJsonValue jv = UniJsonValue.object();
			jv.add("error", "QUERY_IS_MISSING");
			final UniJsonValue videos = UniJsonValue.object();
			jv.add("videos", videos);
			return new Response(200, createStream(jv.toString())).setCustomMime(JSON_MIME);
		}
		String continuation = null;
		try {
			continuation = arg.get("cont");
		} catch (Exception e) {
			continuation = null;
		}
		final String searchTerm = arg.get("query");
		String backend = "youtube-primary";
		try {
			if (arg.containsKey("backend"))
				backend = arg.get("backend");
		} catch (Exception e) {
			backend = "youtube-primary";
		}
		final String searchTerm0 = searchTerm;
		if (backend.equalsIgnoreCase("youtube-primary")) {
			try {
				final YouTubeSearchQuery q = YouTubeSearch.searchFor(searchTerm0, continuation, false);
				Variables.requireNonNull(q, "q");
				final UniJsonValue jv = UniJsonValue.object(5);
				jv.add("continuation", q.getContinuation());
				jv.add("query", q.getSearchQuery());
				jv.add("backend", "youtube-primary");
				/*
				 * placeholder for a future API (suggestions, corrections and empty search
				 * results workaround)
				 */
				jv.add("flags", UniJsonValue.emptyArray());
				final UniJsonValue videos = UniJsonValue.array(q.getResults().size());
				for (final YouTubeSearchResult r : q.getResults()) {
					final UniJsonValue o = UniJsonValue.object();
					if (r.getType() == ResultType.VIDEO) {
						o.add("type", "video");
						o.add("title", r.getVideoTitle());
						o.add("id", r.getVideoID());
						o.add("author", r.getAuthorName());
						o.add("lengthText", r.getLengthText());
						o.add("viewCount", r.getViewCountString());
						o.add("hoverURL", r.getHoverPreviewURL());
						o.add("url", "/watch?v=" + r.getVideoID());
						o.add("thumbnail", "https://i.ytimg.com/vi/" + r.getVideoID() + "/mqdefault.jpg");
					} else if (r.getType() == ResultType.CHANNEL) {
						o.add("type", "channel");
						o.add("id", r.getVideoID());
						o.add("title", r.getVideoTitle());
						if (r.getHoverPreviewURL().startsWith("//")) {
							o.add("thumbnail", "https:" + r.getHoverPreviewURL());
						} else {
							o.add("thumbnail", r.getHoverPreviewURL());
						}
						o.add("url", "/channel?c=" + r.getVideoID());
					} else if (r.getType() == ResultType.PLAYLIST) {
						o.add("type", "playlist");
						o.add("id", r.getVideoID());
						o.add("title", r.getVideoTitle());
						o.add("url", r.getURL());
						o.add("thumbnail", r.getHoverPreviewURL());
						o.add("author", r.getAuthorName());
						o.add("text", r.getLengthText());
					} else if (r.getType() == ResultType.SHOW) {
						o.add("type", "show");
						o.add("id", r.getVideoID());
						o.add("title", r.getVideoTitle());
						o.add("url", r.getURL());
						o.add("thumbnail", "https://i.ytimg.com/vi/" + r.getVideoID() + "/mqdefault.jpg");
						o.add("author", r.getAuthorName());
						o.add("text", r.getLengthText());
					} else {
						log.warn("Unknown type: " + r.getType());
					}
					videos.add(new UniJsonValue(o));
				}
				jv.add("videos", videos);
				q.clear();
				return new Response(200, jv, jv).setCustomMime(JSON_MIME);
			} catch (Exception e) {
				log.error("Search API: Error occured!", e);
				final UniJsonValue jv = UniJsonValue.object();
				jv.add("error", "Internal error: " + e);
				final UniJsonValue videos = UniJsonValue.array();
				jv.add("videos", videos);
				return new Response(200, jv, jv).setCustomMime(JSON_MIME);
			}
		} else {
			final UniJsonValue jv = UniJsonValue.object();
			jv.add("error", "Invalid backend!");
			jv.add("backend", backend);
			return new Response(404, jv, jv).setCustomMime(JSON_MIME);
		}
	}

}
